﻿using System;
using System.Collections.ObjectModel;
using System.IO;
using System.IO.IsolatedStorage;
using System.Linq;
using System.Windows;
using System.Windows.Threading;
using System.Xml.Serialization;
using AgiliTrain.PhoneyTools;
using GalaSoft.MvvmLight;
using Microsoft.Phone.Info;
using Microsoft.Phone.Marketplace;


namespace GooNews.ViewModels
{
  /// <summary>
  /// 
  /// </summary>
  public class MainViewModel : ObservableObject
  {
    const int CACHETIMEINMINUTES = 30;
    const int REFRESHTIMEINMINUTES = 30;
    DispatcherTimer _refreshTimer = new DispatcherTimer();

    /// <summary>
    /// Initializes a new instance of the <see cref="MainViewModel"/> class.
    /// </summary>
    public MainViewModel()
    {
      if (!ViewModelBase.IsInDesignModeStatic)
      {
        if (LastDateRetreived >= DateTime.Now.AddMinutes(-CACHETIMEINMINUTES))
        {
          ReadCache();
        }

        // Turn off timer when locked
        App.ThisApp.RootFrame.Obscured += (s, e) =>
          {
            _isLocked = e.IsLocked;
            if (_refreshTimer.IsEnabled && e.IsLocked)
            {
              _refreshTimer.Stop();
            }
          };

        // Reenable when unlocked
        App.ThisApp.RootFrame.Unobscured += (s, e) =>
          {
            _isLocked = false;
            if (!_refreshTimer.IsEnabled)
            {
              _refreshTimer.Start();
            }
          };

        // Setup Timer for updates
        _refreshTimer.Tick += new EventHandler(_refreshTimer_Tick);
        _refreshTimer.Interval = TimeSpan.FromMinutes(REFRESHTIMEINMINUTES);

        // Show Memory Consuption at first
        ShowMemoryConsuption();

      }
    }

    /// <summary>
    /// Initializes this instance.
    /// </summary>
    internal void Initialize()
    {
      // Load in case read cache fails
      if (Feeds == null)
      {
        RebuildFeeds();
        foreach (var f in Feeds)
        {
          WireFeed(f);
        }
      }
    }

    /// <summary>
    /// Rebuilds the feeds.
    /// </summary>
    public void RebuildFeeds()
    {
      if (Options.FeedTypes.Any(f => f.IsInvalid()))
      {
        _options = OptionsModel.ResetOptions();
        _options.Save();
        MessageBox.Show("Sorry, the upgrade caused a problem with your saved options. We've had to reset your options.  You will need to re-create your search feeds and other options.", "Sorry...", MessageBoxButton.OK);
      }

      var newFeeds = new ObservableCollection<Feed>();

      // Add feeds from options
      foreach (var f in Options.FeedTypes)
      {
        newFeeds.Add(new Feed()
        {
          Title = f.Title,
          Type = f.Type,
          Topic = f.Topic
        });
      }

      this.Feeds = newFeeds;
    }

    /// <summary>
    /// Loads the feed.
    /// </summary>
    /// <param name="index">The index.</param>
    void WireFeed(Feed f)
    {
      f.LoadComplete += new EventHandler(feed_LoadComplete);
    }

    /// <summary>
    /// Handles the LoadComplete event of the feed control.
    /// </summary>
    /// <param name="sender">The source of the event.</param>
    /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
    void feed_LoadComplete(object sender, EventArgs e)
    {
      var f = (Feed)sender;

      // Unwire
      f.LoadComplete -= feed_LoadComplete;

      LastDateRetreived = DateTime.Now;
      WriteCache();
      ShowMemoryConsuption();
      _refreshTimer.Start();
    }

    /// <summary>
    /// Handles the Tick event of the _refreshTimer control.
    /// </summary>
    /// <param name="sender">The source of the event.</param>
    /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
    void _refreshTimer_Tick(object sender, EventArgs e)
    {
      if (App.ThisApp.RootFrame.CurrentSource.ToString().Contains("MainPage.xaml"))
      {
        _refreshTimer.Stop();
        ReloadFeeds();
      }
      ShowMemoryConsuption();
    }

    /// <summary>
    /// Reloads the feeds that are already being loaded.
    /// </summary>
    public void ReloadFeeds()
    {
      foreach (var feed in Feeds.Where(f => f.IsLoaded && !f.IsBusy))
      {
        WireFeed(feed);
        feed.LoadFeedAsync();
      }
    }

    const string LASTRETRIEVEDKEY = "lastRetreived";
    const string CACHEPATH = "FeedsCache.xml";

    /// <summary>
    /// Shows the memory consuption.
    /// </summary>
    internal void ShowMemoryConsuption()
    {
#if SHOWMEMORYINERRORBOX
      long devMem = (long)DeviceExtendedProperties.GetValue("DeviceTotalMemory");
      long curMem = (long)DeviceExtendedProperties.GetValue("ApplicationCurrentMemoryUsage");
      long peakMem = (long)DeviceExtendedProperties.GetValue("ApplicationPeakMemoryUsage");
      ErrorMessage = string.Format("{0:#.##} MB(c)|{1:#.##} MB(p)|{2:#.##} MB(d)",
        curMem / (1024 * 1024.0),
        peakMem / (1024 * 1024.0),
        devMem / (1024 * 1024));
#endif
    }

    /// <summary>
    /// Writes the cache.
    /// </summary>
    private void WriteCache()
    {
      using (var store = IsolatedStorageFile.GetUserStoreForApplication())
      {
        try
        {
          using (var cache = store.CreateFile(CACHEPATH))
          {
            var ser = new XmlSerializer(typeof(ObservableCollection<Feed>));
            ser.Serialize(cache, Feeds);
          }
        }
        catch
        {
          if (store.FileExists(CACHEPATH)) store.DeleteFile(CACHEPATH);
        }
      }
    }

    /// <summary>
    /// Reads the cache.
    /// </summary>
    private void ReadCache()
    {
      using (var store = IsolatedStorageFile.GetUserStoreForApplication())
      {
        try
        {
          if (store.FileExists(CACHEPATH))
          {
            using (var cache = store.OpenFile(CACHEPATH, FileMode.Open))
            {
              var ser = new XmlSerializer(typeof(ObservableCollection<Feed>));
              Feeds = (ObservableCollection<Feed>)ser.Deserialize(cache);
            }
          }
        }
        catch
        {
          if (store.FileExists(CACHEPATH)) store.DeleteFile(CACHEPATH);
        }
      }
    }



    /// <summary>
    /// Navigates to feed.
    /// </summary>
    /// <param name="story">The story.</param>
    public void NavigateToFeed(FeedItem story)
    {
      var feed = Feeds.First(f => f.FeedItems.Contains(story));
      var feedIndex = Feeds.IndexOf(feed);
      var storyIndex = Feeds[feedIndex].FeedItems.IndexOf(story);

      var storyUrl = string.Format("/Pages/ShowSummaryPage.xaml?feed={0}&story={1}", feedIndex, storyIndex);

      App.ThisApp.RootFrame.Navigate(new Uri(storyUrl, UriKind.Relative));
    }

    ObservableCollection<Feed> _feeds = null;

    /// <summary>
    /// Gets the feeds.
    /// </summary>
    public ObservableCollection<Feed> Feeds
    {
      get { return _feeds; }
      private set
      {
        _feeds = value;
        _displayFeeds = null;
        RaisePropertyChanged("Feeds");
        RaisePropertyChanged("DisplayFeeds");
      }
    }

    ObservableCollection<Feed> _displayFeeds = null;

    /// <summary>
    /// Gets the display feeds.
    /// </summary>
    [XmlIgnore]
    public ObservableCollection<Feed> DisplayFeeds
    {
      get
      {
        if (_displayFeeds == null)
        {

          var favFeed = _feeds.Where(f => f.Url == App.ViewModel.Options.FavoriteFeed).FirstOrDefault();
          if (favFeed == null)
          {
            _displayFeeds = Feeds;
          }
          else
          {
            _displayFeeds = new ObservableCollection<Feed>();

            int indexOf = _feeds.IndexOf(favFeed);

            // Add all till end of list
            for (var x = indexOf; x < _feeds.Count; ++x)
            {
              _displayFeeds.Add(_feeds[x]);
            }

            // Add starting ones 
            for (var x = 0; x <= indexOf; ++x)
            {
              _displayFeeds.Add(_feeds[x]);
            }
          }
        }
        return _displayFeeds;
      }
    }

    /// <summary>
    /// Gets or sets the last date retreived.
    /// </summary>
    /// <value>
    /// The last date retreived.
    /// </value>
    public DateTime LastDateRetreived
    {
      get
      {
        if (IsolatedStorageSettings.ApplicationSettings.Contains(LASTRETRIEVEDKEY))
        {
          return (DateTime)IsolatedStorageSettings.ApplicationSettings[LASTRETRIEVEDKEY];
        }
        else
        {
          return DateTime.MinValue;
        }
      }
      set
      {
        IsolatedStorageSettings.ApplicationSettings[LASTRETRIEVEDKEY] = value;
        RaisePropertyChanged("LastDateRetreived");
      }
    }

    OptionsModel _options;

    /// <summary>
    /// Gets the options.
    /// </summary>
    [XmlIgnore]
    public OptionsModel Options
    {
      get
      {
        if (_options == null)
        {
          // Read in Options
          _options = OptionsModel.Load();
        }
        return _options;
      }
    }

    bool _isLocked = false;

    /// <summary>
    /// Gets or sets a value indicating whether this instance is locked.
    /// </summary>
    /// <value>
    ///   <c>true</c> if this instance is locked; otherwise, <c>false</c>.
    /// </value>
    [XmlIgnore]
    public bool IsLocked
    {
      get { return _isLocked; }
      set { _isLocked = value; }
    }

    string _errorMessage
#if DEBUG
 = "(None)"
#endif
;

    /// <summary>
    /// Gets or sets the error message.
    /// </summary>
    /// <value>
    /// The error message.
    /// </value>
    [XmlIgnore]
    public string ErrorMessage
    {
      get { return _errorMessage; }
      set
      {
        _errorMessage = value;
        RaisePropertyChanged("ErrorMessage");
      }
    }

    /// <summary>
    /// Shows the error message.
    /// </summary>
    public void ShowErrorMessage()
    {
      ErrorMessage = "Error While Processing Request";
    }

    bool _isFromActivate = false;

    /// <summary>
    /// Marks as from activate.
    /// </summary>
    internal void MarkAsFromActivate()
    {
      _isFromActivate = true;
    }

    /// <summary>
    /// Determines whether [is directly from activate].
    /// </summary>
    /// <returns>
    ///   <c>true</c> if [is directly from activate]; otherwise, <c>false</c>.
    /// </returns>
    public bool IsDirectlyFromActivate()
    {
      if (_isFromActivate)
      {
        // Only valid once
        _isFromActivate = false;
        return true;
      }

      return false;
    }

  }
}